﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.GraphDb
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// A node in the graph with properties and relationships to other entities.
    /// Along with relationships (<see cref="IRelationship"/>), nodes are the core building
    /// blocks of the Neo4Net data representation model. Nodes are created by invoking
    /// the <see cref="IGraphDatabaseService.CreateNode"/> method.
    /// </summary>
    /// <remarks>
    /// <para>
    /// <c>INode</c> has three major groups of operations: operations that deal with
    /// relationships, operations that deal with properties (see
    /// <see cref="IPropertyContainer"/>) and operations that create <see cref="ITraverser"/>
    /// traversers.
    /// </para>
    /// <para>
    /// The relationship operations provide a number of overloaded methods (such as
    /// <c>GetRelationships(...)</c> with "filters" for type, direction, etc),
    /// as well as the factory method <see cref="CreateRelationshipTo"/>
    /// that connects two nodes with a relationship. It
    /// also includes the convenience method <see cref="GetSingleRelationship"/>
    /// for accessing the commonly occurring one-to-zero-or-one association.
    /// </para>
    /// <para>
    /// The property operations give access to the key-value property pairs. Property
    /// keys are always strings. Valid property value types are all the built-in C#
    /// value types (<c>int</c>, <c>byte</c>, <c>float</c>, etc),
    /// <c>System.String</c>s and arrays of value types and Strings.
    /// </para>
    /// <para>
    /// <b>Please note</b> that Neo4Net does NOT accept arbitrary objects as property
    /// values. <see cref="IPropertyContainer.SetProperty(string, object)"/> takes a
    /// <c>System.Object</c> only to avoid an explosion of overloaded
    /// <c>setProperty()</c> methods. For further documentation see
    /// <see cref="IPropertyContainer"/>.
    /// </para>
    /// <para>
    /// The traversal factory methods instantiate an <see cref="ITraverser"/> that
    /// starts traversing from this node.
    /// </para>
    /// <para>
    /// A node's id is unique, but may not be unique over time since Neo4Net reuses
    /// deleted ids. See <a href="http://wiki.neo4j.org/content/Id_Reuse">
    /// wiki.neo4j.org/content/Id_Reuse</a>.
    /// </para>
    /// </remarks>
    public interface INode : IPropertyContainer
    {
        /// <summary>The unique id of this node. Ids are garbage collected over time
        /// so they are only guaranteed to be unique during a specific time span: if
        /// the node is deleted, it's likely that a new node at some point will get
        /// the old id. <b>Note</b>: this makes node ids brittle as public APIs.
        /// </summary>
        /// <value>The id of this node</value>
        long Id { get; }

        /// <summary>
        /// Deletes this node if it has no relationships attached to it. If
        /// <c>Delete()</c> is invoked on a node with relationships, an
        /// unchecked exception will be raised when the transaction is committing.
        /// Invoking any methods on this node after <c>Delete()</c> has
        /// returned is invalid and will lead to unspecified behavior.
        /// </summary>
        void Delete();

        #region Relationships

        /// <summary>Returns all the relationships attached to this node. If no relationships
        /// are attached to this node, an empty <c>IEnumerable</c> will be returned.
        /// </summary>
        /// <returns>All relationships attached to this node</returns>
        IEnumerable<IRelationship> GetRelationships();

        /// <summary>
        /// Returns <c>true</c> if there are any relationships attached to this
        /// node, <c>false</c> otherwise.
        /// </summary>
        /// <returns> <c>true</c> if there are any relationships attached to this
        ///         node, <c>false</c> otherwise</returns>
        bool HasRelationship();

        /// <summary>
        /// Returns all the relationships of any of the types in <paramref name="types"/>
        /// that are attached to this node, regardless of direction. If no
        /// relationships of the given types are attached to this node, an empty
        /// <c>IEnumerable</c> will be returned.
        /// </summary>
        /// <param name="types">The given relationship type(s)</param>
        /// <returns>All relationships of the given type(s) that are attached to this
        ///         node</returns>
        IEnumerable<IRelationship> GetRelationships(params IRelationshipType[] types);

        /// <summary>
        /// Returns all the relationships of any of the types in <c>types</c>
        /// that are attached to this node and have the given <c>direction</c>.
        /// If no relationships of the given types are attached to this node, an empty
        /// iterable will be returned.
        /// </summary>
        /// <param name="types">The given relationship type(s)</param>
        /// <param name="direction">The direction of the relationships to return.</param>
        /// <returns>All relationships of the given type(s) that are attached to this
        ///         node</returns>
        IEnumerable<IRelationship> GetRelationships(Direction direction, params IRelationshipType[] types);

        /// <summary>
        /// Returns <c>true</c> if there are any relationships of any of the
        /// types in <c>types</c> attached to this node (regardless of
        /// direction), <c>false</c> otherwise.
        /// </summary>
        /// <param name="types">The given relationship type(s)</param>
        /// @return <c>true</c> if there are any relationships of any of the
        ///         types in <c>types</c> attached to this node,
        ///         <c>false</c> otherwise
        bool HasRelationship(params IRelationshipType[] types);

        /// <summary>
        /// Returns <c>true</c> if there are any relationships of any of the
        /// types in <c>types</c> attached to this node (for the given
        /// <c>direction</c>), <c>false</c> otherwise.
        /// </summary>
        /// <param name="types">The given relationship type(s)</param>
        /// <param name="direction">The direction to check relationships for</param>
        /// <returns><c>true</c> if there are any relationships of any of the
        /// given <paramref name="types"/> and <paramref name="direction"/> attached to this node,
        ///         <c>false</c> otherwise</returns>
        bool HasRelationship(Direction direction, params IRelationshipType[] types);

        /// <summary>
        /// Returns all <see cref="Direction.Outgoing"/> or
        /// <see cref="Direction.Incoming"/> relationships from this node. If
        /// there are no relationships with the given direction attached to this
        /// node, an empty <c>IEnumerable</c> will be returned. If <see cref="Direction.Both"/>
        /// is passed in as a direction, relationships of both directions are
        /// returned (effectively turning this into <see cref="GetRelationships()"/>).
        /// </summary>
        /// <param name="direction">The given direction, where <see cref="Direction.Outgoing"/>
        ///            means all relationships that have this node as the start node and
        ///            <see cref="Direction.Incoming"/> means all relationships that have this node as
        ///            the end node</param>
        /// <returns>All relationships with the given direction that are attached to
        ///         this node</returns>
        IEnumerable<IRelationship> GetRelationships(Direction direction);

        /// <summary>
        /// Returns <c>true</c> if there are any relationships in the given
        /// direction attached to this node, <c>false</c> otherwise. If
        /// <see cref="Direction.Both"/> is passed in as a direction, relationships of
        /// both directions are matched (effectively turning this into
        /// <see cref="HasRelationship()"/>).
        /// </summary>
        /// <param name="direction">The given direction, where <see cref="Direction.Outgoing"/>
        ///            means all relationships that have this node as the start node and
        ///            <see cref="Direction.Incoming"/> means all relationships that have this node as
        ///            the end node</param>
        /// <returns><c>true</c> if there are any relationships in the given
        ///         direction attached to this node, <c>false</c> otherwise</returns>
        bool HasRelationship(Direction direction);

        /// <summary>
        /// Returns all relationships with the given type and direction that are
        /// attached to this node. If there are no matching relationships, an empty
        /// iterable will be returned.
        /// </summary>
        /// <param name="type">The given type</param>
        /// <param name="direction">The given direction, where <see cref="Direction.Outgoing"/>
        ///            means all relationships that have this node as the start node and
        ///            <see cref="Direction.Incoming"/> means all relationships that have this node as
        ///            the end node</param>
        /// <returns>All relationships attached to this node that match the given type
        ///         and direction</returns>
        IEnumerable<IRelationship> GetRelationships(IRelationshipType type, Direction direction);

        /// <summary>
        /// Returns <c>true</c> if there are any relationships of the given
        /// relationship type and direction attached to this node, <c>false</c>
        /// otherwise.
        /// </summary>
        /// <param name="type">The given type</param>
        /// <param name="direction">The given direction, where <see cref="Direction.Outgoing"/>
        ///            means all relationships that have this node as the start node and
        ///            <see cref="Direction.Incoming"/> means all relationships that have this node as
        ///            the end node</param>
        /// <returns><c>true</c> if there are any relationships of the given
        ///         relationship type and direction attached to this node,
        ///         <c>false</c> otherwise</returns>
        bool HasRelationship(IRelationshipType type, Direction direction);

        /// <summary>
        /// Returns the only relationship of a given type and direction that is
        /// attached to this node, or <c>null</c>. 
        /// </summary>
        /// <remarks><para>This is a convenience method
        /// that is used in the commonly occuring situation where a node has exactly
        /// zero or one relationships of a given type and direction to another node.
        /// Typically this invariant is maintained by the rest of the code: if at any
        /// time more than one such relationships exist, it is a fatal error that
        /// should generate an unchecked exception. This method reflects that
        /// semantics and returns either:
        /// <list type="number">
        /// <item>
        ///     <description><c>null</c> if there are zero relationships of the given type
        /// and direction,</description></item>
        /// <item><description>the relationship if there's exactly one, or</description></item>
        /// <item><description>throws an unchecked exception in all other cases.</description></item>
        /// </list></para>
        /// <para>
        /// This method should be used only in situations with an invariant as
        /// described above. In those situations, a "state-checking" method (e.g.
        /// <c>HasSingleRelationship(...)</c>) is not required, because this
        /// method behaves correctly "out of the box."
        /// </para>
        /// </remarks>
        /// <param name="type">The type of the wanted relationship</param>
        /// <param name="direction">The direction of the wanted relationship (where <see cref="Direction.Outgoing"/>
        ///            means a relationship that has this node as the start node and
        ///            <see cref="Direction.Incoming"/> means a relationship that has this node as
        ///            the end node) or <see cref="Direction.Both"/> if direction is irrelevant.</param>
        /// <returns>The single relationship matching the given type and direction if
        ///         exactly one such relationship exists, or <c>null</c> if
        ///         exactly zero such relationships exists</returns>
        /// <exception cref="NotFoundException">Thrown if more than one relationship matches the given
        ///             type and direction</exception>
        IRelationship GetSingleRelationship(IRelationshipType type, Direction direction);

        /// <summary>
        /// Creates a relationship between this node and another node. The
        /// relationship is of type <c>type</c>. It starts at this node and
        /// ends at <paramref name="otherNode">otherNode</paramref>>.
        /// </summary>
        /// <remarks>
        /// A relationship is equally well traversed in both directions so there's no
        /// need to create another relationship in the opposite direction (in regards
        /// to traversal or performance).
        /// </remarks>
        /// <param name="otherNode">The end node of the new relationship</param>
        /// <param name="type">The type of the new relationship</param>
        /// <returns>The newly created relationship</returns>
        IRelationship CreateRelationshipTo(INode otherNode, IRelationshipType type);

        #endregion

        #region Expansion

        /* 
        Expansion, tentatively added - save this for a later refactoring

        Expansion<IRelationship> expandAll();

        Expansion<IRelationship> expand( IRelationshipType type );

        Expansion<IRelationship> expand( IRelationshipType type, Direction direction );

        Expansion<IRelationship> expand( Direction direction );

        Expansion<IRelationship> expand( RelationshipExpander expander );
        */

        #endregion

        #region Traversal


        /// <summary>
        /// Instantiates a traverser that will start at this node and traverse
        /// according to the given order and evaluators along the specified
        /// relationship type and direction.
        /// </summary>
        /// <remarks>
        /// <para>If the traverser should traverse more
        /// than one <see cref="IRelationshipType"/>/<see cref="Direction"/> pair, use
        /// one of the overloaded variants of this method. The created traverser will
        /// iterate over each node that can be reached from this node by the spanning
        /// tree formed by the given relationship types (with direction) exactly
        /// once. For more information about traversal, see the <see cref="ITraverser"/>
        /// documentation.
        /// </para>
        /// <para>
        /// Deprecated because of an unnatural and too tight coupling with
        /// <see cref="INode"/>. Also because of the introduction of a new traversal
        /// framework. The new way of doing traversals is by creating an
        /// new <see cref="Traversal.ITraversalDescription"/> from <see cref="Kernel.Traversal.Description()"/>, add rules and
        /// behaviours to it and then calling <see cref="Traversal.ITraversalDescription.Traverse(INode)"/>.
        /// </para>
        /// </remarks>
        /// <param name="traversalOrder">The traversal order</param>
        /// <param name="stopEvaluator">An evaluator instructing the new traverser about
        ///            when to stop traversing, either a predefined evaluator such as
        ///            <see cref="StopEvaluator.EndOfGraph"/> or a custom-written
        ///            evaluator</param>
        /// <param name="returnableEvaluator">An evaluator instructing the new traverser
        ///            about whether a specific node should be returned from the
        ///            traversal, either a predefined evaluator such as
        ///            <see cref="ReturnableEvaluator.All"/> or a customer-written
        ///            evaluator</param>
        /// <param name="relationshipType">The relationship type that the traverser will
        ///            traverse along</param>
        /// <param name="direction">The direction in which the relationships of type
        ///            <paramref name="relationshipType"/> will be traversed</param>
        /// <returns>A new traverser, configured as above</returns>
        [Obsolete]
        ITraverser Traverse(TraversalOrder traversalOrder,
                StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator,
                IRelationshipType relationshipType, Direction direction);

        ///<summary>
        /// Instantiates a traverser that will start at this node and traverse
        /// according to the given order and evaluators along the two specified
        /// relationship type and direction pairs.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If the traverser should traverse
        /// more than two <see cref="IRelationshipType"/>/<see cref="Direction"/> pairs,
        /// use the overloaded
        /// <see cref="Traverse(TraversalOrder, StopEvaluator, ReturnableEvaluator, object[])"/>
        /// variant of this method. The created traverser will iterate over
        /// each node that can be reached from this node by the spanning tree formed
        /// by the given relationship types (with direction) exactly once. For more
        /// information about traversal, see the <see cref="ITraverser"/> documentation.</para>
        /// <para>
        /// Deprecated because of an unnatural and too tight coupling with
        /// <see cref="INode"/>. Also because of the introduction of a new traversal
        /// framework. The new way of doing traversals is by creating an
        /// new <see cref="Traversal.ITraversalDescription"/> from <see cref="Kernel.Traversal.Description()"/>, add rules and
        /// behaviours to it and then calling <see cref="Traversal.ITraversalDescription.Traverse(INode)"/>.
        /// </para>
        /// </remarks>
        /// <param name="traversalOrder">The traversal order</param>
        /// <param name="stopEvaluator">An evaluator instructing the new traverser about
        ///            when to stop traversing, either a predefined evaluator such as
        ///            <see cref="StopEvaluator.EndOfGraph"/> or a custom-written
        ///            evaluator</param>
        /// <param name="returnableEvaluator">An evaluator instructing the new traverser
        ///            about whether a specific node should be returned from the
        ///            traversal, either a predefined evaluator such as
        ///            <see cref="ReturnableEvaluator.All"/> or a customer-written
        ///            evaluator</param>
        /// <param name="firstRelationshipType">The first of the two relationship type that the traverser will
        ///            traverse along</param>
        /// <param name="firstDirection">The direction in which the first relationship type
        ///            will be traversed</param>
        /// <param name="secondRelationshipType">The second of the two relationship type that the traverser will
        ///            traverse along</param>
        /// <param name="secondDirection">The direction in which the second relationship type
        ///            will be traversed</param>
        /// <returns>A new traverser, configured as above</returns>
        // TODO: document the importance of reltype/dir order
        [Obsolete]
        ITraverser Traverse(TraversalOrder traversalOrder,
                StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator,
                IRelationshipType firstRelationshipType, Direction firstDirection,
                IRelationshipType secondRelationshipType, Direction secondDirection);


        /// <summary>
        /// Instantiates a traverser that will start at this node and traverse
        /// according to the given order and evaluators along the specified
        /// relationship type and direction pairs.
        /// </summary>
        /// <remarks>
        /// <para>Unlike the overloaded variants of
        /// this method, the relationship types and directions are passed in as a
        /// <c>params</c> variable-length argument which means that an arbitrary set of
        /// relationship type and direction pairs can be specified. The
        /// variable-length argument list should be every other relationship type and
        /// direction, starting with relationship type, e.g:
        /// </para>
        /// <para>
        /// <c>node.Traverse( TraversalOrder.BreadthFirst, stopEval, returnableEval,
        /// MyRels.Rel1, Direction.Outgoing, MyRels.Rel2, Direction.Outgoing,
        /// MyRels.Rel3, Direction.Both, MyRels.Rel4, Direction.Incoming );</c>
        /// </para>
        /// <para>
        /// Unfortunately, the compiler cannot enforce this so an unchecked exception
        /// is raised if the variable-length argument has a different constitution.
        /// </para>
        /// <para>
        /// The created traverser will iterate over each node that can be reached
        /// from this node by the spanning tree formed by the given relationship
        /// types (with direction) exactly once. For more information about
        /// traversal, see the {@link Traverser} documentation.</para>
        /// <para>
        /// Deprecated because of an unnatural and too tight coupling with
        /// <see cref="INode"/>. Also because of the introduction of a new traversal
        /// framework. The new way of doing traversals is by creating an
        /// new <see cref="Traversal.ITraversalDescription"/> from <see cref="Kernel.Traversal.Description()"/>, add rules and
        /// behaviours to it and then calling <see cref="Traversal.ITraversalDescription.Traverse(INode)"/>.
        /// </para>
        /// </remarks>
        /// <param name="traversalOrder">The traversal order</param>
        /// <param name="stopEvaluator">An evaluator instructing the new traverser about
        ///            when to stop traversing, either a predefined evaluator such as
        ///            <see cref="StopEvaluator.EndOfGraph"/> or a custom-written
        ///            evaluator</param>
        /// <param name="returnableEvaluator">An evaluator instructing the new traverser
        ///            about whether a specific node should be returned from the
        ///            traversal, either a predefined evaluator such as
        ///            <see cref="ReturnableEvaluator.All"/> or a customer-written
        ///            evaluator</param>
        /// <param name="relationshipTypesAndDirections">A variable-length list of
        ///            relationship types and their directions, where the first
        ///            argument is a relationship type, the second argument the first
        ///            type's direction, the third a relationship type, the fourth
        ///            its direction, etc</param>
        /// <returns>A new traverser, configured as above</returns>
        /// <exception cref="ArgumentException">Throws if the variable-length relationship type /
        ///             direction list is not as described above</exception>
        [Obsolete]
        ITraverser Traverse(TraversalOrder traversalOrder,
                StopEvaluator stopEvaluator, ReturnableEvaluator returnableEvaluator,
                params object[] relationshipTypesAndDirections);
        
        #endregion
    }
}
